Handle numeric literal export names in declaration emit#3919
Conversation
Add KindNumericLiteral case to emitModuleExportName in the printer, which was causing a panic when emitting declarations for CommonJS files with numeric export names like `exports[1] = 2`. Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/9f3fe2c6-afe3-4d4b-9709-13e88c7271b6 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
| //// [DtsFileErrors] | ||
|
|
||
|
|
||
| bug.d.ts(2,22): error TS2300: Duplicate identifier '(Missing)'. | ||
| bug.d.ts(2,23): error TS1003: Identifier expected. | ||
| bug.d.ts(2,25): error TS1128: Declaration or statement expected. | ||
| bug.d.ts(4,24): error TS2300: Duplicate identifier '(Missing)'. | ||
| bug.d.ts(4,25): error TS1003: Identifier expected. | ||
| bug.d.ts(4,27): error TS1128: Declaration or statement expected. | ||
| bug.d.ts(6,24): error TS2300: Duplicate identifier '(Missing)'. | ||
| bug.d.ts(6,25): error TS1003: Identifier expected. | ||
| bug.d.ts(6,27): error TS1128: Declaration or statement expected. |
There was a problem hiding this comment.
@copilot Probably this indicates further brokenness, but compare to Strada.
I'd think we'd want to export as "1" etc since numeric props are just number props in disguise? Not sure. Look into it.
There was a problem hiding this comment.
You're right — numeric props are just string props in disguise. Fixed in 0019d09 by converting NumericLiteral to StringLiteral in getNameExpressionPreferringIdentifier in the declaration transformer. Now emits export { _exported as "1" } instead of the invalid export { _exported as 1 }, eliminating the parse errors from the baseline (down from 9 to just 3 duplicate identifier errors, which are expected since all 3 exports share the same name "1").
…ration emit
Numeric property names in JS are string properties; convert NumericLiteral
to StringLiteral in getNameExpressionPreferringIdentifier so declarations
emit valid syntax like `export { _exported as "1" }` instead of the
invalid `export { _exported as 1 }`.
Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/0d9fa6e3-1d16-42fa-824c-c7baf31e67ee
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
| // Numeric property names are string properties in JS; convert to string literal | ||
| nameExpr = tx.Factory().NewStringLiteral(nameExpr.Text(), ast.TokenFlagsNone) | ||
| } | ||
| if ast.IsStringLiteralLike(nameExpr) && scanner.IsIdentifierText(nameExpr.Text(), core.LanguageVariantStandard) { |
There was a problem hiding this comment.
| if ast.IsStringLiteralLike(nameExpr) && scanner.IsIdentifierText(nameExpr.Text(), core.LanguageVariantStandard) { | |
| if ast.IsStringOrNumericLiteralLike(nameExpr) && scanner.IsIdentifierText(nameExpr.Text(), core.LanguageVariantStandard) { |
| } | ||
|
|
||
| func (tx *DeclarationTransformer) getNameExpressionPreferringIdentifier(nameExpr *ast.Node) *ast.Node { | ||
| if ast.IsNumericLiteral(nameExpr) { |
There was a problem hiding this comment.
This change isn't correct or necessary. Just use isStringOrNumericLiteralLike in the check below.
|
@copilot Please make the requested changes. |
|
Nevermind on the above comments. I agree that converting numeric literals to string literals is the best option here, though it is different from what Strada does. Strada converts the numeric literal |
There was a problem hiding this comment.
Pull request overview
This PR fixes declaration emit for CommonJS numeric export names by converting numeric property names into string-literal export names and adding regression coverage.
Changes:
- Converts numeric CommonJS export names to string literals during declaration transformation.
- Updates module export-name printing behavior.
- Adds a compiler test and baselines for numeric export declarations.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
internal/transformers/declarations/transform.go |
Converts numeric export property names to string literals before declaration emit. |
internal/printer/printer.go |
Adds numeric literal handling in module export-name emission. |
testdata/tests/cases/compiler/numericExportNameDeclaration.ts |
Adds regression input covering numeric CommonJS export patterns. |
testdata/baselines/reference/compiler/numericExportNameDeclaration.js |
Adds emitted declaration baseline and dts diagnostics. |
testdata/baselines/reference/compiler/numericExportNameDeclaration.symbols |
Adds symbol baseline for the new test. |
testdata/baselines/reference/compiler/numericExportNameDeclaration.types |
Adds type baseline for the new test. |
| case ast.KindNumericLiteral: | ||
| p.emitNumericLiteral(node.AsNumericLiteral()) |
There was a problem hiding this comment.
Fixed in 4304c9f. The KindNumericLiteral case in emitModuleExportName now emits the numeric literal as a quoted string literal ("1" instead of bare 1), since ModuleExportName syntax only accepts identifiers or string literals.
There was a problem hiding this comment.
@copilot what? That's not what I asked, the comment was saying the code was dead and I asked you to confirm
There was a problem hiding this comment.
Confirmed — the KindNumericLiteral case was dead code. The declaration transformer's getNameExpressionPreferringIdentifier (line 2176) converts all numeric literals to string literals before they reach the printer. The parser's parseModuleExportName/canParseModuleExportName only produces identifiers or string literals, never numeric literals. Removed the dead case in 95b66cc.
… printer Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/1ec9206d-7bdc-4410-a06d-8ea7247652c9 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/4efd5625-7022-4615-84fe-f09fae670b06 Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
The printer panics when emitting declarations for CommonJS files with numeric export names (e.g.
exports[1] = 2).emitModuleExportNameonly handledKindIdentifierandKindStringLiteral, missingKindNumericLiteral. Additionally, the declaration transformer passed numeric literals through unchanged, producing invalid syntax likeexport { _exported as 1 }.getNameExpressionPreferringIdentifierin the declaration transformer, since numeric property names in JS are string properties — now emits validexport { _exported as "1" }syntaxemitModuleExportNameexports[1],module.exports[1], andObject.defineProperty(exports, 1, {})